/*
* GPL
* Free soft,no warranted
* We are all in the mud, but some of us are looking at the stars.
* We are not born for ourselves.
*/
/**
* @file service_connection_pool.hpp
* @Synopsis
* @author Chai JunJun
* @date 2009-07-10
*/

#ifndef QSERVER_SERVICE_CONNECTION_POOL_HPP__
#define QSERVER_SERVICE_CONNECTION_POOL_HPP__

#include <boost/assert.hpp>
#include <boost/asio/detail/mutex.hpp>
#include <boost/noncopyable.hpp>
#include <vector>

#include "netconnection.hpp"

namespace qserv 
{

    #define QSERV_GRACEFUL_CLOSED_WAIT_DELAY 5

    /// A pool of NetConnection objects.
    template<typename Work_Handler, typename Work_Allocator, typename Socket_Service = boost::asio::ip::tcp::socket>
    class NetConnPool
        : private boost::noncopyable
    {
    public:
        /// The type of the NetConnection.
        typedef NetConnection<Work_Handler, Socket_Service> netconnection_type;
        typedef boost::shared_ptr< netconnection_type > netconnection_ptr;

        /// The type of the work_allocator.
        typedef Work_Allocator work_allocator_type;
        typedef boost::shared_ptr< work_allocator_type > work_allocator_ptr;

        /// Construct the NetConnection pool.
        explicit NetConnPool(work_allocator_ptr work_allocator,
            std::size_t initial_pool_size,
            std::size_t timeout_seconds = 0,
            std::size_t closed_wait_delay = QSERV_GRACEFUL_CLOSED_WAIT_DELAY)
            : m_netConns(),
            m_workAllocator(work_allocator),
            m_initialPoolSize(initial_pool_size),
            m_timeout(timeout_seconds),
            m_closedWaitDelay(closed_wait_delay),
            m_nextConnection(0)
        {
            BOOST_ASSERT(work_allocator != 0);
            BOOST_ASSERT(initial_pool_size != 0);

            // Create preallocated service_handler pool.
            for (std::size_t i = 0; i < m_initialPoolSize; ++i)
            {
                netconnection_ptr service_handler(makeConn());
                m_netConns.push_back(service_handler);
            }
        }

        /// Destruct the pool object.
        ~NetConnPool()
        {
            for (std::size_t i = 0; i < m_netConns.size(); ++i)
                m_netConns[i].reset();
            m_netConns.clear();

            m_workAllocator.reset();
        }

        /// Get an service_handler to use.
        netconnection_ptr getServConn(boost::asio::io_service& io_service,
            boost::asio::io_service& work_service)
        {
            netconnection_ptr connptr;

            // Check the next handler is busy or not.
            if (!m_netConns[m_nextConnection]->is_busy())
            {
                connptr = m_netConns[m_nextConnection];
                if (++m_nextConnection == m_netConns.size())
                    m_nextConnection = 0;
            }
            else
                m_nextConnection = 0;

            // If the next handler is busy, create new handler.
            if (connptr.get() == 0)
            {
                connptr.reset(makeConn());
                m_netConns.push_back(connptr);
            }

            // Bind the NetConnection with given io_service and work_service.
            connptr->bind(io_service, work_service, m_workAllocator.get());
            return connptr;
        }

        /// Get an NetConnection with the given mutex.
        netconnection_ptr getServConn(boost::asio::io_service& io_service,
            boost::asio::io_service& work_service,
            boost::asio::detail::mutex& mutex)
        {
            // For client handler, need lock in multiple thread model.
            boost::asio::detail::mutex::scoped_lock lock(mutex);
            return getServConn(io_service, work_service);
        }

        const std::vector<netconnection_ptr>& getAllConns() const 
        { 
            return m_netConns; 
        }

    private:
        /// Make one NetConnection.
        netconnection_type* makeConn()
        {
            return new netconnection_type(m_workAllocator->make_handler(),
                m_timeout,
                m_closedWaitDelay);
        }

    private:
        /// The pool of preallocated NetConnection.
        std::vector<netconnection_ptr> m_netConns;

        /// The allocator of work_handler.
        work_allocator_ptr m_workAllocator;

        /// Preallocated NetConnection number.
        std::size_t m_initialPoolSize;

        /// The expiry seconds of connection.
        std::size_t m_timeout;

        /// The delay seconds before reuse again.
        std::size_t m_closedWaitDelay;

        /// The next NetConnection to use for a connection.
        std::size_t m_nextConnection;
    };

} // namespace qserv

#endif // QSERVER_SERVICE_CONNECTION_POOL_HPP__
